package com.xlh.security;

import com.google.common.collect.Lists;
import com.xlh.constant.UserConstant;
import com.xlh.enums.user.IdentityEnum;
import com.xlh.exception.SmsException;
import com.xlh.pojo.system.ImageCode;
import com.xlh.pojo.user.User;
import com.xlh.properties.ProjectProperties;
import com.xlh.util.RequestUtil;
import com.xlh.util.SpringContextUtil;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.bind.ServletRequestBindingException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * 短信验证码拦截器
 * <p>
 * Created by lx on 2020/3/10.
 */
@Data
public class SmsCodeFilter extends AbstractAuthenticationProcessingFilter {

    private AuthenticationFailureHandler authenticationFailureHandler;

    private static ProjectProperties projectProperties = SpringContextUtil.getBean(ProjectProperties.class);

    private AuthenticationManager authenticationManager;

    public SmsCodeFilter(String pageUrl, String httpMethod) {
        super(new AntPathRequestMatcher(pageUrl, httpMethod));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {

        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String code = request.getParameter("mobileCode");

        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authentication = null;
        try {
            authentication = this.authenticationManager.authenticate(token);
            User user = ((CustomUserDetails) authentication.getPrincipal()).getUser();
            validate(request, user, code);
        } catch (AuthenticationException e) {
            authenticationFailureHandler.onAuthenticationFailure(request, response, e);
            return null;
        }

        super.setContinueChainBeforeSuccessfulAuthentication(true);
        return authentication;

    }

    private void validate(HttpServletRequest request, User user, String code) throws ServletException, SmsException, IOException {

        if (!projectProperties.getInternet()
                || IdentityEnum.getByRole(user.getRole()) != IdentityEnum.TEACHER
                || StringUtils.isEmpty(user.getTelephoneNumber())) return;

        // 如果手机号不一致，需要删除验证码
        HttpSession session = request.getSession(true);
        if (!Objects.equals(user.getTelephoneNumber(),
                (String) session.getAttribute(UserConstant.MOBILE_TEL_SESSION_KEY))) {
            session.removeAttribute(UserConstant.SMS_CODE_SESSION_KEY);
        }

        // 如果ip变动，需要短信验证码进行验证
        String ip = RequestUtil.getIpAddress(request);
        List<String> dbIps = StringUtils.isBlank(user.getIp()) ?
                Lists.newArrayList() : Lists.newArrayList(user.getIp().split(","));

        if (!dbIps.contains(ip)) {
            try {
                validateCode(code, (ImageCode) session.getAttribute(UserConstant.SMS_CODE_SESSION_KEY));
            } catch (SmsException e) {
                session.removeAttribute(UserConstant.MOBILE_TEL_SESSION_KEY);
                session.setAttribute(
                        UserConstant.MOBILE_TEL_SESSION_KEY, user.getTelephoneNumber());
                throw e;
            }
        }

    }

    private void validateCode(String code, ImageCode sessionCode) throws ServletRequestBindingException {
        if ((sessionCode == null && StringUtils.isBlank(code))
                || StringUtils.isBlank(code)) {
            throw new SmsException("请输入短信验证码");
        }

        if (sessionCode == null && StringUtils.isNotBlank(code)) {
            throw new SmsException("短信验证码不正确，请重新获取");
        }

        if (sessionCode.isExpired()) {
            throw new SmsException("短信验证码已失效，请重新获取");
        }

        if (!sessionCode.getCode().equalsIgnoreCase(code)) {
            throw new SmsException("短信验证码不正确");
        }

    }

}